﻿#region Using Statements
using System;
using GameBase.Particles;
using GameBase.Screens;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion

namespace StarFox.ScreenComponents
{
    /// <summary>
    /// Implementation of a colored smoke particle effect.
    /// </summary>
    public class ColorSmoke2D : ScreenComponent3D, IDisposable
    {
        #region Fields

        const int NUM_SMOKE = 3;
        ParticleSystem2D[] smokeParticle;
        Vector3[] smokePosition;
        BlendState blendState;

        Color[] Colors = new Color[9]
        {
            Color.Aqua,
            Color.Blue,
            Color.Chartreuse,
            Color.Crimson,
            Color.DeepPink,
            Color.Fuchsia,
            Color.Gold,
            Color.OrangeRed,
            Color.Yellow
        };

        readonly int MAX_PARTICLES = 150;
        readonly TimeSpan MAX_DURATION = TimeSpan.FromSeconds(6);
        readonly TimeSpan TIME_START = TimeSpan.FromSeconds(4);

        TimeSpan timer;
        bool isStart = true;
        int[] numParticles;
        int index;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of this class.
        /// </summary>
        /// <param name="screen">The Screen that this ScreenComponent3D is associated to.</param>
        public ColorSmoke2D(Screen screen)
            : base(screen)
        {
            // alphablend
            blendState = new BlendState()
            {
                ColorSourceBlend = Blend.SourceAlpha,
                AlphaSourceBlend = Blend.SourceAlpha,
                ColorDestinationBlend = Blend.One,
                AlphaDestinationBlend = Blend.One,
            };

            smokeParticle = new ParticleSystem2D[NUM_SMOKE];
            for (int i = 0; i < NUM_SMOKE; i++)
            {
                smokeParticle[i] = new ParticleSystem2D(Screen.SpriteBatch, MAX_PARTICLES)
                {
                    Texture = Screen.Content.Load<Texture2D>(ContentSettings.GetAsset("PARTICLES_TEXTURE")),
                    Duration = MAX_DURATION,
                    MinVelocity = 15,
                    MaxVelocity = 30,
                    EndVelocity = 0.75f,
                    MinStartSize = 0.0f,
                    MaxStartSize = 1.0f,
                    MinEndSize = 0.0f,
                    MaxEndSize = 0.0f,
                    BlendState = blendState,
                };
            }

            smokePosition = new Vector3[NUM_SMOKE];
            for (int i = 0; i < NUM_SMOKE; i++) { smokePosition[i] = Vector3.Zero; }

            numParticles = new int[NUM_SMOKE];
            for (int i = 0; i < NUM_SMOKE; i++) { numParticles[i] = MAX_PARTICLES; }
        }

        #endregion

        #region Dispose

        // dispose flag
        bool isDisposed = false;

        /// <summary>
        /// Immediately releases all resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
        /// </summary>
        ~ColorSmoke2D()
        {
            Dispose(false);
        }

        /// <summary>
        /// Releases the unmanaged resources used by this object and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!isDisposed)
            {
                if (disposing)
                {
                    if (blendState != null) { blendState.Dispose(); blendState = null; }
                    for (int i = 0; i < NUM_SMOKE; i++)
                    {
                        if (smokeParticle[i] != null) { smokeParticle[i].Dispose(); smokeParticle[i] = null; }
                    }
                }

                isDisposed = true;
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// Called when the ScreenComponent3D needs to be updated.
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to this method.</param>
        public override void Update(GameTime gameTime)
        {
            timer += gameTime.ElapsedGameTime;
            if (timer > TIME_START)
            {
                timer -= TIME_START;
                isStart = true;
            }
            
            // adjust view matrix
            Matrix smokeView = Matrix.Invert(Camera.View);
            smokeView.Translation = Vector3.Zero;

            if (isStart)
            {
                isStart = false;
                numParticles[index] = 0;

                // random color
                int rndClr = StarFoxHelper.RandomInt(Colors.Length);
                smokeParticle[index].MaxColor = new Color(new Vector4(Colors[rndClr].ToVector3(), 255));
                smokeParticle[index].MinColor = new Color(new Vector4(Colors[rndClr].ToVector3(), 127));

                // adjust position
                Vector3 auxPos = -2000.0f * Vector3.UnitZ + new Vector3(PointOnCircle(500.0f), 0.0f);
                smokePosition[index] = Vector3.Transform(auxPos, smokeView);

                // random gravity
                Vector3 auxGravity = StarFoxHelper.RandomVector(0.0f, 10.0f);
                smokeParticle[index].Gravity = new Vector2(auxGravity.X, auxGravity.Y);

                index = (index + 1) % NUM_SMOKE;
            }

            // adjust view matrix
            smokeView = Matrix.Invert(smokeView);

            for (int i = 0; i < NUM_SMOKE; i++)
            {
                Vector3 smokeProjection = GraphicsDevice.Viewport.Project(smokePosition[i], Camera.Projection, smokeView, Matrix.Identity);
                smokeParticle[i].Position = new Vector2(smokeProjection.X, smokeProjection.Y);
            }

            for (int i = 0; i < NUM_SMOKE; i++)
            {
                if (numParticles[i] < MAX_PARTICLES)
                {
                    Vector2 auxPosition = PointOnCircle(5.0f);
                    smokeParticle[i].AddParticle(auxPosition, Vector2.Zero);
                    numParticles[i]++;
                }

                smokeParticle[i].Update(gameTime);
            }
        }

        /// <summary>
        /// Positions a Particle2D on the border of a circle.
        /// </summary>
        /// <param name="radius">The radius of the circle.</param>
        /// <returns>Position of the Particle2D.</returns>
        Vector2 PointOnCircle(float radius)
        {
            double angle = StarFoxHelper.RandomFloat(0.0f, MathHelper.TwoPi);
            return new Vector2(radius * (float)Math.Cos(angle), radius * (float)Math.Sin(angle));
        }

        #endregion

        #region Draw

        /// <summary>
        /// Called when the ScreenComponent3D needs to be updated.
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to this method.</param>
        public override void Draw(GameTime gameTime)
        {
            for (int i = 0; i < NUM_SMOKE; i++)
            {
                smokeParticle[i].Draw(gameTime);
            }
        }

        #endregion
    }
}
